import { CodeGenerate, CodeGenerateField, FieldTypeInterfaceMap, FieldTypeSchemaTypeMap, LookupListItem } from "@/types/admin/codeGenerate";
import lodash from "lodash";

export const getTypeCode = (data: Omit<CodeGenerate, "fields"> & { fields: CodeGenerateField[] }) => {
    const { fields, modelName } = data;
    return `${
        fields.some(item => FieldTypeInterfaceMap[item.type] === "ObjectId") ? 'import { ObjectId } from "mongoose";\n\n' : ""
    }export interface ${modelName} {
    ${fields.map((item: CodeGenerateField) => `${item.name}: ${FieldTypeInterfaceMap[item.type]}`).join(";\n\t")}${fields.length ? ";" : ""}
}`;
};

export const getModelCode = (data: Omit<CodeGenerate, "fields"> & { fields: CodeGenerateField[] }, resultType: "admin" | "client") => {
    const { fields, modelName, collectionName } = data;
    return `import { model, Schema, Types } from "mongoose";
import { ${modelName} } from '@/types/${resultType}/${lodash.lowerFirst(modelName)}';

const ${modelName}Schema = new Schema<${modelName}>(
    {
        ${fields
            .map(
                item =>
                    `${item.name}: { type: ${FieldTypeSchemaTypeMap[item.type]}${item.required ? ", required: true" : ""}${
                        item.unique ? ", unique: true" : ""
                    } }`
            )
            .join(",\n\t\t")}
    },
    { timestamps: { createdAt: true, updatedAt: true } }
);

export const ${modelName}Model = model("${modelName}", ${modelName}Schema${collectionName ? `, '${collectionName}'` : ""});`;
};

export const getPageListCode = (data: Omit<CodeGenerate, "fields"> & { fields: CodeGenerateField[] }) => {
    const { fields, modelName, lookupList } = data;
    const fuzzyFields = fields.filter((item: CodeGenerateField) => item.queryType === "fuzzy");
    const preciseFields = fields.filter((item: CodeGenerateField) => item.queryType === "precise");
    const matchFields = [...fuzzyFields, ...preciseFields];
    const haveSort = fields.filter(item => item.sortType).length > 0;
    const noFindFields = fields.filter(item => !item.isTable && !item.isForm).map(item => item.name);
    const $project = `${noFindFields?.length ? `\n\t\t$project: { ${noFindFields.map(item => `${item}: 0`).join(", ")} },` : ""}`;
    let $lookList = lookupList
        ?.map(item => {
            return `\n\t\t\t{ from: "${item.from}", localField: "${item.localField}", foreignField: ${`"${item.foreignField}"` || "_id"}, as: "${
                item.as || item.localField
            }" },`;
        })
        .join("");
    $lookList = $lookList?.length ? `\n\t\t$lookupList: [${$lookList}\n\t\t]` : "";
    return `export const get${modelName}ListService: Service = async (req, res) => {
    const { page, pageSize, ...match } = req.query;
    const { ${matchFields.map(item => item.name).join(", ")} } = match;
    const options: GetPageListOptions<${modelName}> = {
        model: ${modelName}Model,
        params: req.query as PageReq,${
            haveSort
                ? `\n$sort: { ${fields
                      .filter(item => item.sortType)
                      .map(item => `${item.name}: ${item.sortType === "asc" ? 1 : -1}`)
                      .join(", ")} }`
                : ""
        }${$project}${$lookList}
    };
    ${matchFields.length ? `if (${matchFields.map(item => item.name).join(" || ")}) options.$match = {};` : ""}
    ${
        preciseFields.length
            ? `${preciseFields
                  .map(
                      (item: CodeGenerateField) =>
                          `if(![undefined,null,''].includes(${item.name} as undefined | null | string)) options.$match!.${item.name} = ${item.name}`
                  )
                  .join("\n\t")};`
            : ""
    }
    ${matchFields.length ? "if (options.$match) {" : ""}
    ${fuzzyFields
        .map(
            (item: CodeGenerateField) =>
                `\tif(${item.name} && typeof ${item.name} === "string") options.$match.${item.name} = { $regex: RegExp(${item.name}), $options: "i" };`
        )
        .join("\n\t")}
    ${matchFields.length ? "}" : ""}
    catchError({
        async operation() {
            const { total, list } = await getPageList(options);
            res.sendResponse!(200, { total, list });
        },
        res,
        type: "查询",
    });
}`;
};

export const getDetailCode = (data: Omit<CodeGenerate, "fields"> & { fields: CodeGenerateField[] }) => {
    const { modelName, lookupList } = data;
    return `export const get${modelName}DetailService: Service = async (req, res) => {
    const { _id } = req.params;
    const childFields:GetRecordOptions<${modelName}>['childFields'] = ${
        lookupList?.length
            ? `[
        ${lookupList.map(
            item =>
                `{ localField: "${item.localField}", from: "${item.from}", foreignField: "${item.foreignField || "_id"}", as: "${
                    item.as || item.localField
                }" }`
        )}
    ]`
            : "[]"
    };
    catchError({
        async operation() {
            return await getRecord({ model: ${modelName}Model, _id, childFields });
        },
        res,
        type: "查询",
    });
};`;
};

export const getCreateCode = (data: Omit<CodeGenerate, "fields"> & { fields: CodeGenerateField[] }) => {
    const { modelName, lookupList } = data;
    return `export const create${modelName}Service: Service = async (req, res) => {
    const { ${lookupList?.length ? `${lookupList.map(item => item.localField).join(", ")}, ` : ""}...data } = req.body;
    catchError({
        async operation() {
            const childFields:CreateRecordOptions<${modelName}>['childFields'] = ${
        lookupList?.length
            ? `[
                ${lookupList.map(item => `{ key: "${item.localField}", value: ${item.localField}, model: ${item.modelName}Model }`)}
            ]`
            : "[]"
    };
            return await createRecord({ model: ${modelName}Model, childFields, data });
        },
        res,
        type: "创建",
    });
};`;
};

export const getUpdateCode = (data: Omit<CodeGenerate, "fields"> & { fields: CodeGenerateField[] }) => {
    const { modelName, lookupList } = data;
    return `export const update${modelName}Service: Service = async (req, res) => {
    const { _id, ${lookupList?.length ? `${lookupList.map(item => item.localField).join(", ")}, ` : ""}...data } = req.body;
    catchError({
        async operation() {
            const childFields:UpdateRecordOptions<${modelName}>['childFields'] = ${
        lookupList?.length
            ? `[
                ${lookupList.map(item => `{ key: "${item.localField}", value: ${item.localField}, model: ${item.modelName}Model }`)}
            ]`
            : "[]"
    };
            return await updateRecord({ model: ${modelName}Model, _id, childFields, data });
        },
        res,
        type: "更新",
    });
};`;
};

export const getDeleteCode = (data: Omit<CodeGenerate, "fields"> & { fields: CodeGenerateField[] }) => {
    const { modelName, lookupList } = data;
    return `export const delete${modelName}Service: Service = async (req, res) => {
    const { _id } = req.body;
    const parentFields:DeleteRecordOptions<${modelName}>['parentFields'] = ${
        lookupList?.length
            ? `[
        ${lookupList.map(item => `{ key: "${item.localField}", model: ${item.modelName}Model }`)}
    ]`
            : "[]"
    };
    catchError({
        async operation() {
            return await deleteRecord({model: ${modelName}Model, _id, parentFields});
        },
        res,
        type: "删除",
    });
};`;
};

export const getParentFindCode = (data: Omit<CodeGenerate, "fields"> & { fields: CodeGenerateField[] }) => {
    const { fields, modelName, parentFind } = data;
    if (!parentFind) return "";
    const { localField, parentModelName } = parentFind;
    const fuzzyFields = fields.filter((item: CodeGenerateField) => item.queryType === "fuzzy");
    const preciseFields = fields.filter((item: CodeGenerateField) => item.queryType === "precise");
    const matchFields = [...fuzzyFields, ...preciseFields];
    const haveSort = fields.filter(item => item.sortType).length > 0;
    return `export const getItemsService: Service = async (req, res) => {
    const { _id, ...match } = req.query;
    const { ${matchFields.map(item => item.name).join(", ")} } = match;
    const options: GetChildrenPageListOptions<${modelName}> = {
        model: ${modelName}Model,
        params: req.query as PageReq,${
            haveSort
                ? `\n$sort: { ${fields
                      .filter(item => item.sortType)
                      .map(item => `${item.name}: ${item.sortType === "asc" ? 1 : -1}`)
                      .join(", ")} },`
                : ""
        }
        parentKey: "_id",
        parentValue: _id,
        parentModel: ${parentModelName}Model,
        localField: "${localField}",
        foreignField: "_id",
    };
    ${matchFields.length ? `if (${matchFields.map(item => item.name).join(" || ")}) options.$match = {};` : ""}
    ${
        preciseFields.length
            ? `${preciseFields
                  .map(
                      (item: CodeGenerateField) =>
                          `if(![undefined,null,''].includes(${item.name} as undefined | null | string)) options.$match!.${item.name} = ${item.name}`
                  )
                  .join("\n\t")};`
            : ""
    }
    ${matchFields.length ? "if (options.$match) {" : ""}
        ${fuzzyFields
            .map(
                (item: CodeGenerateField) =>
                    `if(typeof ${item.name} === "string") options.$match.${item.name} = { $regex: RegExp(${item.name}), $options: "i" };`
            )
            .join("\n\t\t")}
    ${matchFields.length ? "}" : ""}
    catchError({
        async operation() {
            const result = await getChildrenPageList(options);
            res.sendResponse!(200, result);
        },
        res,
        type: "查询",
    });
};`;
};

export const getServiceCode = (data: Omit<CodeGenerate, "fields"> & { fields: CodeGenerateField[] }, resultType: "admin" | "client") => {
    const { lookupList, operations, modelName, parentFind } = data;
    const serviceFunctionStr = [
        ["getPageList", getPageListCode],
        ["getDetail", getDetailCode],
        ["create", getCreateCode],
        ["update", getUpdateCode],
        ["delete", getDeleteCode],
        ["parentFind", getParentFindCode],
    ]
        .filter(([op]) => operations.includes(op as CodeGenerate["operations"][number]))
        .map(([_, fn]) => (fn as Function)(data))
        .join("\n");

    const serviceUtilImportCode = [
        ["create", "createRecord"],
        ["delete", "deleteRecord"],
        ["getPageList", "getPageList"],
        ["getPageList", "GetPageListOptions"],
        ["getDetail", "getRecord"],
        ["update", "updateRecord"],
        ["parentFind", "GetChildrenPageListOptions"],
        ["parentFind", "getChildrenPageList"],
        ["getDetail", "GetRecordOptions"],
        ["create", "CreateRecordOptions"],
        ["update", "UpdateRecordOptions"],
        ["delete", "DeleteRecordOptions"],
    ]
        .filter(([condition]) => operations.includes(condition as CodeGenerate["operations"][number]))
        .map(([_, code]) => code)
        .join(",\n\t");
    return `import { Service } from "@/middleware/response";
import { ${modelName}Model } from "@/models/${resultType}/${modelName}";
import { ${modelName} } from "@/types/${resultType}/${lodash.lowerFirst(modelName)}";
import { PageReq } from "@/types/common/common";
import {
    catchError,
    ${serviceUtilImportCode}
} from "@/utils/db";
${parentFind?.parentModelName ? `import { ${parentFind.parentModelName}Model } from "@/models/${resultType}/${parentFind.parentModelName}";` : ""}
${lookupList?.map((item: LookupListItem) => `import { ${item.modelName}Model } from "@/models/${resultType}/${item.modelName}";`).join("\n")}

${serviceFunctionStr}`;
};

export const getControllerCode = (data: CodeGenerate & { fields: CodeGenerateField[] }, resultType: "admin" | "client") => {
    const { operations, modelName, parentFind, pageName, fields } = data;
    const controllerImportServiceCode = [
        [operations.includes("create"), `create${modelName}Service,`],
        [operations.includes("delete"), `delete${modelName}Service,`],
        [operations.includes("getPageList"), `get${modelName}ListService,`],
        [operations.includes("getDetail"), `get${modelName}DetailService,`],
        [operations.includes("update"), `update${modelName}Service,`],
        [operations.includes("parentFind") && !!parentFind?.parentModelName, "getItemsService"],
    ]
        .filter(([condition]) => condition)
        .map(([_, code]) => code)
        .join("\n\t");
    const ResultType = { admin: "管理端", client: "客户端" };
    const lowerFirstName = lodash.lowerFirst(modelName);
    const tagNameEnds = pageName?.endsWith("管理") ? pageName.slice(0, -2) : pageName;
    const tags = `['${ResultType[resultType]}${tagNameEnds}']`;
    const fuzzyFields = fields.filter((item: CodeGenerateField) => item.queryType === "fuzzy");
    const preciseFields = fields.filter((item: CodeGenerateField) => item.queryType === "precise");
    const matchFields = [...fuzzyFields, ...preciseFields];
    const getParamType = (field: CodeGenerateField) => {
        const TypeMap = {
            Number: "number",
            Boolean: "boolean",
            ObjectIdArray: "array",
            NumberArray: "array",
            StringArray: "array",
            BooleanArray: "array",
            ObjectArray: "array",
        };
        return TypeMap[field.type as keyof typeof TypeMap] || "string";
    };
    const getQueryArgs = () => {
        return matchFields
            .map(item => {
                return `\t\t#swagger.parameters['${item.name}'] = { in: 'query', description: '${item.label}', type: '${getParamType(item)}' }`;
            })
            .join("\n");
    };
    const getItems = (field: CodeGenerateField) => {
        const ItemsMap = {
            NumberArray: ", items: { type: 'number' }",
            StringArray: ", items: { type: 'string' }",
            BooleanArray: ", items: { type: 'boolean' }",
            ObjectArray: ", items: { type: 'object', additionalProperties: true }",
            ObjectIdArray: ", items: { type: 'string' }",
        }
        return ItemsMap[field.type as keyof typeof ItemsMap] || "";
    };

    const getBodyArgs = (isUpdate = false) => {
        return [
            `\t\t#swagger.requestBody = {`,
            `\t\t\trequired: true,`,
            `\t\t\tcontent: {`,
            `\t\t\t\t"application/json": {`,
            `\t\t\t\t\tschema: {`,
            `\t\t\t\t\t\ttype: "object",`,
            `\t\t\t\t\t\trequired: ${isUpdate ? "['_id']" : `[${fields.filter(item => item.required).map(item => `'${item.name}'`)}]`},`,
            `\t\t\t\t\t\tproperties: {${isUpdate ? `\n\t\t\t\t\t\t\t_id: { description: '${tagNameEnds}ID', type: 'string' },` : ""}`,
            `${fields
                .map(field => `\t\t\t\t\t\t\t${field.name}: { description: '${field.label}', type: '${getParamType(field)}'${getItems(field)} }`)
                .join(",\n")}`,
            `\t\t\t\t\t\t}`,
            `\t\t\t\t\t}`,
            `\t\t\t\t}`,
            `\t\t\t}`,
            `\t\t}`,
        ].join("\n");
    };
    const routesCode = [
        [
            operations.includes("getPageList"),
            [
                `router.get("/", (req, res) => {`,
                `\t/*`,
                `\t\t#swagger.tags = ${tags}`,
                `\t\t#swagger.path = '/admin-api/${lowerFirstName}'`,
                `\t\t#swagger.description = '获取${tagNameEnds}分页数据'`,
                `\t\t#swagger.security = [{"bearerAuth": []}]`,
                `\t\t#swagger.parameters['page'] = { in: 'query', description: '页号', type: 'integer' }`,
                `\t\t#swagger.parameters['pageSize'] = { in: 'query', description: '每页个数', type: 'integer' }`,
                `${getQueryArgs()}`,
                `\t*/`,
                `\tget${modelName}ListService(req, res);`,
                `});`,
            ].join("\n"),
        ],
        [
            operations.includes("getDetail"),
            [
                `router.get("/:_id", (req, res) => {`,
                `\t/*`,
                `\t\t#swagger.tags = ${tags}`,
                `\t\t#swagger.path = '/admin-api/${lowerFirstName}/{_id}'`,
                `\t\t#swagger.description = '获取指定${tagNameEnds}'`,
                `\t\t#swagger.security = [{"bearerAuth": []}]`,
                `\t\t#swagger.parameters['_id'] = { in: 'path', description: '${tagNameEnds}ID', type: 'string', required: true }`,
                `\t*/`,
                `\tget${modelName}DetailService(req, res);`,
                `});`,
            ].join("\n"),
        ],
        [
            operations.includes("parentFind") && !!parentFind?.parentModelName,
            [
                `router.get("/items", (req, res) => {`,
                `\t/*`,
                `\t\t#swagger.tags = ${tags}`,
                `\t\t#swagger.path = '/admin-api/${lowerFirstName}/items'`,
                `\t\t#swagger.description = '父查子分页'`,
                `\t\t#swagger.security = [{"bearerAuth": []}]`,
                `\t\t#swagger.parameters['page'] = { in: 'query', description: '页号', type: 'integer' }`,
                `\t\t#swagger.parameters['pageSize'] = { in: 'query', description: '每页个数', type: 'integer' }`,
                `${getQueryArgs()}`,
                `\t*/`,
                `\tgetItemsService(req, res);`,
                `});`,
            ].join("\n"),
        ],
        [
            operations.includes("create"),
            [
                `router.post("/", (req, res) => {`,
                `\t/*`,
                `\t\t#swagger.tags = ${tags}`,
                `\t\t#swagger.path = '/admin-api/${lowerFirstName}'`,
                `\t\t#swagger.description = '创建${tagNameEnds}'`,
                `\t\t#swagger.security = [{"bearerAuth": []}]`,
                `${getBodyArgs()}`,
                `\t*/`,
                `\tcreate${modelName}Service(req, res);`,
                `});`,
            ].join("\n"),
        ],
        [
            operations.includes("update"),
            [
                `router.put("/", (req, res) => {`,
                `\t/*`,
                `\t\t#swagger.tags = ${tags}`,
                `\t\t#swagger.path = '/admin-api/${lowerFirstName}'`,
                `\t\t#swagger.description = '更新${tagNameEnds}'`,
                `\t\t#swagger.security = [{"bearerAuth": []}]`,
                `${getBodyArgs(true)}`,
                `\t*/`,
                `\tupdate${modelName}Service(req, res);`,
                `});`,
            ].join("\n"),
        ],
        [
            operations.includes("delete"),
            [
                `router.delete("/", (req, res) => {`,
                `\t/*`,
                `\t\t#swagger.tags = ${tags}`,
                `\t\t#swagger.path = '/admin-api/${lowerFirstName}'`,
                `\t\t#swagger.description = '删除${tagNameEnds}'`,
                `\t\t#swagger.security = [{"bearerAuth": []}]`,
                `\t\t#swagger.requestBody = {`,
                `\t\t\trequired: true,`,
                `\t\t\tcontent: {`,
                `\t\t\t\t"application/json": {`,
                `\t\t\t\t\tschema: {`,
                `\t\t\t\t\t\ttype: "object",`,
                `\t\t\t\t\t\trequired: ['_id'],`,
                `\t\t\t\t\t\tproperties: {`,
                `\t\t\t\t\t\t\t_id: { description: '${tagNameEnds}ID', type: 'string' },`,
                `\t\t\t\t\t\t}`,
                `\t\t\t\t\t}`,
                `\t\t\t\t}`,
                `\t\t\t}`,
                `\t\t}`,
                `\t*/`,
                `\tdelete${modelName}Service(req, res);`,
                `});`,
            ].join("\n"),
        ],
    ]
        .filter(([condition]) => condition)
        .map(([_, code]) => code)
        .join("\n");
    return `import {
    ${controllerImportServiceCode}
} from "@/services/${resultType}/${lowerFirstName}";
import express from "express";

const router = express.Router();

${routesCode}

export default router;`;
};
